package cn.tb.ymcc.service.impl;

import cn.tb.ymcc.domain.Course;
import cn.tb.ymcc.domain.CourseMarket;
import cn.tb.ymcc.domain.KillActivity;
import cn.tb.ymcc.domain.KillCourse;
import cn.tb.ymcc.dto.KillParamDto;
import cn.tb.ymcc.dto.KillPreOrderParamsDto;
import cn.tb.ymcc.mapper.KillCourseMapper;
import cn.tb.ymcc.service.IKillActivityService;
import cn.tb.ymcc.service.IKillCourseService;
import cn.tb.ymcc.util.AssertUtil;
import cn.tb.ymcc.util.CodeGenerateUtils;
import cn.tb.ymcc.vo.CourseItemVo;
import cn.tb.ymcc.vo.CourseOrderVo;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author tangbo
 * @since 2022-08-04
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    IKillActivityService killActivityService;

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    /**
     * 为活动增加秒杀课程
     * @param killCourse
     */
    @Override
    public void save(KillCourse killCourse) {
        KillCourse killCourseTmp = selectByIdAndCourseID(killCourse.getActivityId(), killCourse.getCourseId());
        AssertUtil.isNull(killCourseTmp,"秒杀课程已经存在！");

        //同一个课程不能加入到同一个活动下面
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setCreateTime(new Date());
        insert(killCourse);
    }

    private KillCourse selectByIdAndCourseID(Long activityId, Long courseId) {
        Wrapper<KillCourse> query = new EntityWrapper<>();
        query.eq("course_id",courseId);
        query.eq("activity_id",activityId);
        return selectOne(query);
    }

    /**
     * 查询所有秒杀课程
     * @return
     */
    @Override
    public List<KillCourse> onlineAll() {
        List<KillCourse> killCourses = new ArrayList<>();
        //拿到指定指定前缀的key
        Set<Object> keys = redisTemplate.keys("activity:*");
        keys.forEach(bigKey->{
            List values = redisTemplate.opsForHash().values(bigKey);
            killCourses.addAll(values);
        });
        return killCourses;
    }

    /**
     * 查询秒杀课程详情
     * @param activityId
     * @param killId
     * @return
     */
    @Override
    public KillCourse onlineOne(Long activityId, Long killId) {
        AssertUtil.isNotNull(activityId,"请求非法");
        AssertUtil.isNotNull(killId,"请求非法");
        return (KillCourse) redisTemplate.opsForHash().get("activity:"+activityId, killId.toString());
    }

    /**
     * 执行秒杀
     *
     * 1.查询出秒杀课程
     *  === 是否在秒杀中  ==
     *  必须要登录
     *  一个人不能重复秒杀同一个课程
     *
     * 2.使用信号量扣减库存
     * 3.扣减不成功，给客人一个提示信息，秒杀结束---稍后重试！
     * 4.如果扣减成功--生成预订单，保存到redis-返回订单号
     * @param dto
     * @return
     */
    @Override
    public String kill(KillParamDto dto) {
        Long activityId = dto.getActivityId();
        Long killCourseId = dto.getKillCourseId();
        //查询秒杀课程,判断课程是否在秒杀中
        KillCourse killCourse = onlineOne(dto.getActivityId(), dto.getKillCourseId());
        AssertUtil.isTrue(killCourse.isKilling(),"没有秒杀课程");
        //必须要登录
        Long loginId = 3L;
        //一个人不能重复秒杀同一个课程
        //当第一次点秒杀，使用当前活动id+秒杀id+登录人id作为key值，存储Redis
        String repeatKey = activityId +":"+killCourseId +":"+loginId;
        Object repeatOrder = redisTemplate.opsForValue().get(repeatKey);
        AssertUtil.isNull(repeatOrder,"请勿重复秒杀！！");
        //使用信号量扣减库存
        String key = activityId +":"+killCourseId;
        RSemaphore semaphore = redissonClient.getSemaphore(key);

        Integer killCount = 1;
        boolean b = semaphore.tryAcquire(killCount);
        //扣减信号量不成功，提示
        AssertUtil.isTrue(b,"手速太慢了，没有了");
        //扣除信号量成功，生成预订单，保存到Redis
        String orderNo = CodeGenerateUtils.generateOrderSn(loginId);
        KillPreOrderParamsDto killPreOrderParamsDto = new KillPreOrderParamsDto(
                orderNo,
                killCount,
                killCourse.getKillPrice(),
                loginId,
                killCourse.getCourseId(),
                killCourse.getCourseName(),
                killCourse.getCoursePic(),
                dto.getActivityId(),
                dto.getKillCourseId()

        );
        redisTemplate.opsForValue().set(orderNo,killPreOrderParamsDto);

        //存储秒杀成功信息，防止重复秒杀
        redisTemplate.opsForValue().set(repeatKey,orderNo);
        return orderNo;
    }

    /**
     * 秒杀订单确认页面数据回显
     * @param orderNo
     * @return
     */
    @Override
    public CourseOrderVo oneByOrder(String orderNo) {
        //1.从Redis中获取预订单
        KillPreOrderParamsDto killPreOrder = (KillPreOrderParamsDto)redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(killPreOrder,"没有预订单");
        //2.设置前端需要展示的vo
        List<CourseItemVo> courseInfos = new ArrayList<>();
        //课程相关
        Course course = new Course();
        course.setId(killPreOrder.getCourseId());
        course.setName(killPreOrder.getCourseName());
        course.setPic(killPreOrder.getCoursePic());
        //课程销售相关
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(killPreOrder.getKillPrice());

        CourseItemVo courseItemVo = new CourseItemVo(course,courseMarket);
        courseInfos.add(courseItemVo);
        //将封装好的List<CourseItemVo> + 总金额，封装成CourserOrderVo
        return new CourseOrderVo(courseInfos,killPreOrder.getKillPrice());
    }
}
